<?php
/*
* Kusht Mud.
* ----
* The MIT License
* Copyright (c) 2010 Andrew Tutt
* ----
*/

/* Basic Server Class
*
* Designed to Abstract the Most Basic Connection Functionality
*/

/* There is a bug in Chrome currently where refresh does not disconnect it's socket.
	 They are fixing it. It does make one concerned about the possibility of nasty disconnected sockets
	 staying on the server. It will be fixed at some point. We trust them too much methinks. */

class BasicServer {

private $master;	 		/* THE Asynch Listening Socket */
private $sockets;			/* Array of Connected Sockets	*/
private $connectors;		/* Independently handled connectors */
private $address;			/* Server Addr */
private $port;		 		/* Server Port */

function __construct($address = "localhost", $port=12345) {
	error_reporting(E_ALL);
	set_time_limit(0);
	ob_implicit_flush();
		
	$this->address 		= $address;
	$this->port 		= $port;
		
	$this->master		= $this->WebSocket($address,$port);
	$this->sockets 		= array('m'=>$this->master);
	$this->connectors	= array();
}

function handle_connections() {
	$master = $this->master;
	$changed = $this->sockets;	
	
	count($changed) > 1 ? $block = 0 : $block = NULL; // No one playing THEN sleep
	
	// Trailing Zero means non-blocking. Trailing NULL would mean block.
	socket_select($changed,$write=NULL,$except=NULL, $block);

	foreach($changed as $key=>$socket){
		if($socket===$master){
			$client=socket_accept($master) or die("socket_accept() failed");
			$this->connect($client);
			continue;
		}
		
		$connector = $this->connectors[$key];
		
		$bytes = @socket_recv($socket,$buffer,2048,0);
		if($bytes===0) {
			$connector->disconnect();
			continue;
		}
		
		$connector->handle_input($buffer);

	}
}

function WebSocket($address,$port) {
	$master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)	or die("socket_create() failed");
	socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1)	or die("socket_option() failed");
	socket_bind($master, $address, $port)					or die("socket_bind() failed");
	socket_listen($master,20)								or die("socket_listen() failed");
	echo "\n---\n";
	echo "Server Started: ".date('Y-m-d H:i:s')."\n";
	echo "Master socket : ".$master."\n";
	echo "Listening on  : ".$address." port ".$port."\n";
	echo "---\n";
	return $master;
}

function connect($socket){
	socket_set_nonblock($socket);
	usleep(75000); // Give time for Socket to Write
	$bytes = @socket_recv($socket,$buffer,2048,0);
	
	if(ord($buffer) == 71) { // This is a Google Chrome Connection
		$this->JSONConnect($socket,$buffer);
		return;
	}
	
	$this->TelnetConnect($socket);
}

function JSONConnect($socket,$buffer) {
	$connector = new JSONConnector($this,$socket);
	$key = $connector->id;
	$this->connectors	+= array($key=>$connector);
	$this->sockets		+= array($key=>$socket);
	echo $socket." CONNECTED.\n";
	$connector->handle_input($buffer);
}

function TelnetConnect($socket) {
	$connector = new TelnetConnector($this,$socket);
	$key = $connector->id;
	$this->connectors	+= array($key=>$connector);
	$this->sockets		+= array($key=>$socket);
	echo $socket." CONNECTED.\n";
	$connector->handle_input("");
}

function disconnect($key,$socket){
	unset($this->connectors[$key]);
	unset($this->sockets[$key]);
	@socket_close($socket); // Sometimes the sockets close on their own, hence we use @ to cover it
}

}

?>
